package small.common.loadBalance.radom;

import small.common.loadBalance.AbstractLoadBalance;
import small.common.loadBalance.BalanceInvoker;
import small.common.loadBalance.BalanceNode;

import java.util.List;
import java.util.concurrent.ThreadLocalRandom;

/**
 * 随机权重
 * @author jinzhou
 * @data 2023/4/2 13:00
 */
public class RandomLoadBalance<T extends BalanceNode,V extends BalanceInvoker> extends AbstractLoadBalance<T,V> {

    @Override
    protected T doSelect(List<T> nodes,V invoker) {

        // 计算数量
        int length = nodes.size();
        // 相同权重标识
        boolean sameWeight = true;
        // 存储所有权重
        int[] weights = new int[length];
        // 总权重
        int totalWeight = 0;
        for (int i = 0; i < length; i++) {
            int weight = nodes.get(i).getWeight();
            // 累加权重
            totalWeight += weight;
            //检测当前服务提供者的权重与上一个服务提供者的权重是否相同，
            //不相同的话，则将 sameWeight 置为 false。
            weights[i] = totalWeight;
            if (sameWeight && totalWeight != weight * (i + 1)) {
                sameWeight = false;
            }
        }
        // 下面的 if 分支主要用于获取随机数，并计算随机数落在哪个区间上
        if (totalWeight > 0 && !sameWeight) {
            // 随机获取一个 [0, totalWeight) 区间内的数字
            int offset = ThreadLocalRandom.current().nextInt(totalWeight);
            // 循环让 offset 数减去服务提供者权重值，当 offset 小于0时，返回相应的 Invoker。
            // 举例说明一下，我们有 servers = [A, B, C]，weights = [5, 3, 2]，offset = 7。
            // 第一次循环，offset - 5 = 2 > 0，即 offset > 5，
            // 表明其不会落在服务器 A 对应的区间上。
            // 第二次循环，offset - 3 = -1 < 0，即 5 < offset < 8，
            // 表明其会落在服务器 B 对应的区间上
            for (int i = 0; i < length; i++) {
                if (offset < weights[i]) {
                    return nodes.get(i);
                }
            }
        }
        // 如果所有服务提供者权重值相同，此时直接随机返回一个即可
        return nodes.get(ThreadLocalRandom.current().nextInt(length));
    }

}
